<!--
 * @Author: dongdong.wu
 * @Date: 2021-03-15 14:15:12
 * @Description: 表格组件
-->
<script>
import { summaryMethod, rowSpanMethod, colSpanMethod } from "./utils.js";
import renderPage from "./we-table-page.js";
import renderColumnCheckBox from "./we-checkbox-column";
import renderOperatorColumn from "./we-operator-column";
import { columnOptions, renderColumn } from "./we-table-column";
import commonCss from "@/styles/common.scss";
const elementResizeDetectorMaker = require("element-resize-detector");
import Sortable from "sortablejs";
import renderTableHeaderFilter from "./we-table-header-filter.js";
export default {
  name: "WeTable",
  props: {
    // 表头数据
    headers: {
      /**
       * prop：行数据字典
       * label:显示的表头
       * width：对应列宽
       * headerAlign:表头对齐方式
       * align：表数据对齐方式
       * fixed:是否固定列
       * sortable:是否排序
       * min-width:对应最小列宽
       */
      type: Array,
      default: () => {
        return [];
      }
    },
    // 表体数据
    list: {
      type: [Array, String],
      default: () => {
        return [];
      }
    },
    // 是否开启根据计算可视范围内，固定表头
    // FIXME 非window 改变无法监听！
    hasResize: {
      type: Boolean,
      default: false
    },
    // 是否增加拖动列.同时开启之后增加row-key.否则会出现渲染问题。
    dragDisabled: {
      type: Boolean,
      default: true
    },
    // 是否开启checkbox
    selection: {
      type: Boolean,
      default: false
    },
    // 仅对 type=selection 的列有效，类型为 Boolean，为 true 则会在数据更新之后保留之前选中的数据（需指定 row-key）
    reserveSelection: {
      type: Boolean,
      default: false
    },
    // 表格ref。
    tableRef: {
      type: String,
      default: "we-table"
    },
    sumText: String,
    // 操作
    //列名
    btnLabel: {
      type: String,
      default: "操作"
    },
    // 操作列宽
    btnWidth: {
      type: Number,
      default: 150
    },
    // 分页
    // 是否开启分页
    isPage: {
      type: Boolean,
      default: true
    },
    // 当前页数
    page: {
      type: Number,
      default: 1
    },
    // 每页显示条目个数
    rows: {
      type: Number,
      default: 15
    },
    // 总条目数
    total: {
      type: [Number, String],
      default: 0
    },
    // 每页显示个数选择器的选项设置
    pagerCount: Number,
    layout:{
      type:String,
      default: "total,sizes,prev,pager,next,jumper"
    },
    // 是否开启多表头的合计。一般来说一级表头，为了性能不去开启。
    // TODO 为了性能后面直接提供模板？少量多级性能几乎忽略不计，是否合适待定。
    levelColumnsSummary: {
      type: Boolean,
      default: false
    },
    // 合并行设置
    spanOption: {
      type: Object,
      default: () => {
        return {
          spanIndex: [], // 合并列数。默认为空，为空则表示不合并
          spanKey: "id" // 数据合并唯一标识。默认以id作为合并标识
        };
      }
    },
    // 合并列
    colSpanOption: {
      type: Object,
      default: () => {
        return {
          isColSpan: false, // 是否开启合并
          colSpan: 1 // 列合并数
        };
      }
    },
    // 是否开启table组件输入框跳转等。默认关闭
    hasPressEnter: {
      type: Boolean,
      default: false
    },
    hasPressBlur: {
      type: Boolean,
      default: false
    },
    // 跳转焦点校验字段的方法。其方法名为prop+Rule。其中保存的方法固定为save
    validate: {
      type: Object,
      default: () => {
        return {};
      }
    },
    // 是否显示控制头部列表的按钮组件
    hasHeaderFilter: {
      type: Boolean,
      default: false
    },
    // 保存表头过滤的缓存key
    headerFilterKey: {
      type: String,
      default: "headerFilter"
    }
  },
  data() {
    return {
      tableHeight: null,
      defaultCheckedHeaderFilter: []
    };
  },
  computed: {
    tableList() {
      if (this.list == "success") {
        return [];
      } else {
        return this.list;
      }
    },
    headerList() {
      return [...this.headers];
    },
    filterHeader() {
      return [...this.headers];
    },
    // 获取多级表头数据
    levelColumns() {
      return this.levelColumnsSummary ? this.getLevelColumns(this.headers) : "";
    }
  },
  created() {
    if (this.hasHeaderFilter) {
      this.initDeafaultCheckHeader();
      this.initDeafaultHeaders();
    }
  },
  mounted() {
    if (this.hasResize) {
      this.resizeTable();
    }
    this.dragTable();
    if (this.hasPressEnter) {
      this.addCellKeyEvent();
    }
    if (this.hasPressBlur) {
      this.addCellBlurValid();
    }
  },
  methods: {
    addCellKeyEvent() {
      this.$nextTick(() => {
        const tableDom = this.$refs[this.tableRef].$el;
        // 这里只对el-input进行监听移动下一个焦点
        let self = this;
        $(tableDom).on("keyup", ".el-input", function (event) {
          const e = event || window.event;
          if (e.keyCode == 13) {
            const currentEl = event.currentTarget;
            const notDisabled = $(currentEl).attr("disabled");
            const elInputEl = $(currentEl).find(".el-input__inner");
            const currentParentTdEl = $(currentEl).parents(".ele-table_cell");
            const currentParentTrEl = $(currentParentTdEl).parent();
            const nextAllEl = currentParentTdEl.nextAll();
            if (!notDisabled && elInputEl) {
              // 不是disabled 且是输入框。则直接移动下一个
              const rowIndex = $(currentEl)
                .parents(".ele-table-column--slot")
                .attr("data-index");
              const validateRule = $(currentEl)
                .parents(".ele-table-column--slot")
                .attr("data-slot");
              // 当前行数据
              const row = self.tableList[rowIndex];
              if (self.validate[validateRule]) {
                // 存在字段校验
                self.validate[validateRule](row)
                  .then(() => {
                    // 当前节点验证数据成功之后才进行移动
                    self.moveFocus(nextAllEl, row, currentParentTrEl);
                  })
                  .catch(err => {
                    // 校验提醒
                    self.$warning(err);
                  });
              } else {
                // 不存在，直接移动
                self.moveFocus(nextAllEl, row, currentParentTrEl);
              }
            }
          }
        });
      });
    },
    addCellBlurValid() {
      this.$nextTick(() => {
        const tableDom = this.$refs[this.tableRef].$el;
        // 这里只对el-input进行监听移动下一个焦点
        let self = this;
        $(tableDom).on("blur ", ".el-input", function (event) {
          const currentEl = event.currentTarget;
          const rowIndex = $(currentEl)
            .parents(".ele-table-column--slot")
            .attr("data-index");
          const validateRule = $(currentEl)
            .parents(".ele-table-column--slot")
            .attr("data-slot");
          const row = self.tableList[rowIndex];
          if (self.validate[validateRule]) {
            const rowFiled = validateRule.substring(0,validateRule.length-4)
            // 存在字段校验
            self.validate[validateRule](row[rowFiled])
              .then(() => {
                if (currentEl.parentNode.getElementsByClassName("el-form-item__error").length >0) {
                  currentEl.parentNode.removeChild(currentEl.parentNode.getElementsByClassName("el-form-item__error")[0])
                }
              })
              .catch(err => {
                if (currentEl.parentNode.getElementsByClassName("el-form-item__error").length === 0) {
                  // 校验提醒
                  var dom = document.createElement("div");
                  dom.className = "el-form-item__error";
                  dom.innerHTML = err.message;
                  currentEl.parentNode.appendChild(dom);
                }
              });
          }
        });
      });
    },
    moveFocus(nextAllEl, row, currentParentTrEl) {
      // 移动焦点
      let self = this;
      nextAllEl.each(function () {
        // 非禁用的输入框
        const nextInputEl = $(this).find(".el-input__inner:not(:disabled)");
        // 保存按钮,必须title为保存才可
        const btnEl = $(this).find("i[title=保存]");
        if (nextInputEl.length > 0) {
          // 移动焦点。下一个输入框进行移动
          nextInputEl.focus();
          return false;
        }
        // 下面是按钮。则进行新增并回到初始化
        if (btnEl.length > 0) {
          self.validate
            .save(row)
            .then(() => {
              // 当前节点验证数据成功之后才进行移动
              // FIXME 因为加了row-key，el-table在渲染的时候是整个dom。所以导致focus是当前的focus。而不是第一个\
              // 所以暂定使用 $setFocus('#index+props')
              //   self.setFirstFocus(currentParentTrEl);
            })
            .catch(err => {
              // 校验提醒
              self.$warning(err);
            });
          return false;
        }
      });
    },
    /**
     * 设置初始化焦点
     */
    setFirstFocus(el) {
      const firstTr = el.first();
      firstTr.find(".ele-table_cell").each(function () {
        const nextInputEl = $(this).find(".el-input__inner:not(:disabled)");
        if (nextInputEl) {
          console.log(nextInputEl);
          nextInputEl.focus();
          return false;
        }
      });
    },
    //  多级表头获取真实子节点数据
    getLevelColumns(columns) {
      let newColumns = [];
      columns.forEach(i => {
        if (i.child && i.child.length > 0) {
          newColumns = [...newColumns, ...this.getLevelColumns(i.child)];
        } else {
          newColumns.push(i);
        }
      });
      return newColumns;
    },
    headerFilterCheck(selectNode, { checkedNodes }) {
      let unCheckedNodes = [];
      this.headerList = this.headers.filter(i => {
        const checkedNode = checkedNodes.some(n => {
          return n.prop == i.prop && n.label == i.label;
        });
        if (!checkedNode) {
          // 保存不可见的表头prop
          unCheckedNodes.push(i.prop);
        }
        return checkedNode;
      });
      this.setHeaderFilter(unCheckedNodes);
    },
    // 初始化默认选中过滤器
    initDeafaultCheckHeader() {
      const levelHeaders = this.getLevelColumns(this.headers);
      const unCheckedNodeList = this.getHeaderFilter();
      levelHeaders.forEach(i => {
        if (!unCheckedNodeList || !unCheckedNodeList.includes(i.prop)) {
          this.defaultCheckedHeaderFilter.push(i.prop);
        }
      });
    },
    // 初始化可显示的表头。
    initDeafaultHeaders() {
      const unCheckedNodeList = this.getHeaderFilter();
      this.headerList = this.headers.filter(i => {
        return !(unCheckedNodeList && unCheckedNodeList.includes(i.prop));
      });
    },
    // 保存不可见的表头
    setHeaderFilter(unCheckedNodes) {
      localStorage.setItem(
        this.headerFilterKey,
        JSON.stringify(unCheckedNodes)
      );
    },
    // 获取不可见的表头
    getHeaderFilter() {
      return JSON.parse(localStorage.getItem(this.headerFilterKey));
    },
    resizeTable() {
      // 可视高度减去padding和分页组件的高度
      const mainBodyPadding = commonCss.mainBodyPadding.split("p")[0];
      const tableDom = this.$refs[this.tableRef].$el;
      const erd = elementResizeDetectorMaker();
      const self = this;
      const pageHeight = this.isPage ? 45 : 0;
      // 监听table dom 变化设置最大高度
      // FIXME 在收缩查询条件栏时，无法触发该监听
      erd.listenTo(tableDom, function (element) {
        const tableOffsetTop = element.offsetTop;
        console.log(tableOffsetTop);
        self.$nextTick(function () {
          this.tableHeight =
            window.innerHeight -
            tableOffsetTop -
            (Number(mainBodyPadding) + pageHeight);
        });
      });
    },
    dragTable() {
      const el = this.$refs[this.tableRef].$el.querySelectorAll(
        ".el-table__body-wrapper > table > tbody"
      )[0];
      this.sortable = Sortable.create(el, {
        disabled: this.dragDisabled,
        setData: function (dataTransfer) {
          dataTransfer.setData("Text", "");
        },
        onEnd: ({ newIndex, oldIndex }) => {
          const currRow = this.list.splice(oldIndex, 1)[0];
          this.list.splice(newIndex, 0, currRow);
          // 对于某些情况，需要拖拽后的时候，则同时影响父组件list数据
          this.$emit("update:list", this.list);
        }
      });
    },
    handleSizeChange(val) {
      this.$emit("update:rows", val);
      this.$emit("page-rows-change");
    },
    handlePageChange(val) {
      this.$emit("update:page", val);
      this.$emit("page-rows-change");
    },
    summaryMethod(param) {
      // 合计行
      return summaryMethod(
        param,
        this.headers,
        this.sumText,
        this.levelColumns
      );
    },
    spanMethod(param) {
      if (this.spanOption.spanIndex.length > 0) {
        return rowSpanMethod(param, this.spanOption, this.list);
      } else if (this.colSpanOption.isColSpan) {
        return colSpanMethod(param, this.colSpanOption);
      }
    },
    /**
     * @description: 用于多选表格，切换某一行的选中状态，如果使用了第二个参数，则是设置这一行选中与否
     * @param {Number} row 行数据
     * @param {Boolean} selectType 是否勾选
     */
    toggleRowSelection(row, isSelected = true) {
      this.$refs[this.tableRef].toggleRowSelection(row, isSelected);
    },
    /**
     * @description 用于多选表格，清空用户的选择
     */
    clearSelection() {
      this.$refs[this.tableRef].clearSelection();
    },
    /**
     * @param {Number} row 行数据
     * @param {*} index
     * @description 用于处理在type selection时，是否可选。所以返回的list数据要清洗数据添加isChecked字段
     */
    selectable(row) {
      return row.isChecked == undefined || row.isChecked == true ? true : false;
    }
  },
  render(h) {
    // ele-table的基础信息
    // 设置el-table props
    const tableOptions = {
      style: {
        width: "100%"
      },
      props: {
        "max-height": this.tableHeight,
        ...this.$attrs,
        ...this.$props,
        data: this.tableList,
        cellClassName: "ele-table_cell",
        headerCellClassName: "ele-table_header--cell",
        spanMethod: this.spanMethod,
        summaryMethod: this.summaryMethod,
        border: true,
        fit: true,
        stripe: true,
        sumText: this.sumText,
        highlightCurrentRow: true
      },
      scopedSlots: {
        // 空数据时
        // empty: () => {
        //   return h("i", {
        //     class: "welink welink-empty"
        //   });
        // }
        empty: () => {
          let url = require('../../../assets/images/body/dataNo.png')
          return h("el-image", {
            class:'empty-img',
            props: {
              src: url
            }
          });
        }
      },
      on: {
        ...this.$listeners
      },
      ref: this.tableRef
    };
    return h(
      "div",
      {
        class: "ele-table_container"
      },
      [
        h("el-table", tableOptions, [
          renderColumnCheckBox.call(this, h),
          ...this.headerList.map(item => {
            return h("el-table-column", columnOptions.call(this, h, item), [
              renderColumn.call(this, h, item)
            ]);
          }),
          renderOperatorColumn.call(this, h)
        ]),
        renderPage.call(this, h),
        renderTableHeaderFilter.call(this, h)
      ]
    );
  }
};
</script>
<style lang="scss" scoped>
.ele-table_container {
  position: relative;
  background-color: #fff;
  ::v-deep {
    .el-table__fixed-right {
      height: calc(100%) !important;
    }
    .el-table__fixed-right::before {
      height: 0px !important;
    }
    .ele-table_cell {
      padding: 10px 0px;
      color: #333333;
    }
    .ele-table_header--cell {
      padding: 8px 0px;
      background-color: #f5f7fa;
      color: #333333;
    }
    .el-table__empty-text {
      align-items: center;
      justify-content: center;
      /* height: 100%; */
      display: flex;
      padding: 40px;
      i {
        font-size: 100px;
        color: var(--component-color, #0084CF);
      }
    }
    .ele-table_operator {
      i {
        font-size: 18px;
        margin-right: 10px;
        color: var(--component-color);
      }
      &:hover {
        cursor: pointer;
      }
    }
    .ele-table_pagination {
      padding-left: 10px;
      box-sizing: border-box;
      height: 45px;
      display: flex;
      align-items: center;
      font-size: 12px;
      color: #666;
      border: 1px solid #ebeef5;
      border-radius: 4px;
      border-top-left-radius: 0px;
      border-top-right-radius: 0px;
      border-top: none;
      .el-pagination__total {
        font-size: 12px;
      }
      .el-pagination__sizes .el-input .el-input__inner {
        font-size: 12px;
      }
      .el-pagination__jump {
        font-size: 12px;
      }
    }
  }
  .ele-table-header-filter_icon {
    position: absolute;
    right: 0;
    top: 0;
    padding: 5px;
    z-index: 6;
    background-color: #ffffff;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    cursor: pointer;
  }
}
.empty-img {
  width: 200px;
  height: 180px;
  padding: 20px;
}
</style>
